#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

static pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
static pthread_mutex_t mtx = PTHREAD_MUTEX_INITIALIZER;
static int glob = 0;

static void cleanup_handler(void *arg)
{
  int s;
  printf("cleanup: freeing block at %p\n",arg);
  free(arg);

  printf("cleanup: unlocking mutex\n");
  s = pthread_mutex_unlock(&mtx);
  if (s != 0)
    printf("Failed to unlock mutex\n");
}

static void* thread_func(void *arg)
{
  int s;
  void *buf = NULL;
  buf = malloc(0x10000);
  printf("thread: allocated memory at %p\n",buf);

  s = pthread_mutex_lock(&mtx);
  if (s != 0)
    printf("Failed to lock mutex\n");

  pthread_cleanup_push(cleanup_handler, buf);

  while (glob == 0) {
    s = pthread_cond_wait(&cond,&mtx);
    if (s != 0)
      printf("Failed to wait a condvar\n");
  }
  
  printf("thread: condition wait loop completed\n");
  pthread_cleanup_pop(1);
  return NULL;
}

int main(int argc, char *argv[])
{
  pthread_t thread;
  void *res;
  int s;

  s = pthread_create(&thread, NULL, thread_func, NULL);
  if (s != 0)
    printf("Failed to create thread\n");

  sleep(2);

  if (argc == 1) {
    printf("%s :about to cancel thread\n",__func__);
    s = pthread_cancel(thread);
    if (s != 0)
      printf("Failed to cancel thread\n");
  } 
  else {
    printf("%s :about to signal condition variable\n",__func__);
    pthread_mutex_lock(&mtx);
    glob = 1;
    pthread_mutex_unlock(&mtx);
    s = pthread_cond_signal(&cond);
    if (s != 0)
      printf("Failed to signal condition variable\n");
  }

  s = pthread_join(thread, &res);
  if (s != 0)
    printf("Failed to join a thread\n");
  
  if (res == PTHREAD_CANCELED)
    printf("%s :thread was canceled\n",__func__);
  else
    printf("%s :thread terminated normally\n",__func__);
  
  return 0;
}
